﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using PerfCounter;
using System.IO;

namespace PerformanceTest
{
    namespace CPU
    {
        public class CPUPerformance
        {
            public double UnitInMs { get; set; }

            private const long vaCost = 42;
            private const long mmCost = 76;
            private const long mvCost = 60;

            public CPUPerformance()
            {
                string path = "./cpuperf.txt";
                if (!File.Exists(path))
                {
                    StreamWriter sw = new StreamWriter(path);
                    TestPerformance();
                    sw.WriteLine(UnitInMs);
                    sw.Close();
                }
                else
                {
                    StreamReader sr = new StreamReader(path);
                    UnitInMs = Double.Parse(sr.ReadLine());
                    sr.Close();
                }
            }

            public void TestPerformance()
            {                    
                double unit = 0.0;
                int size = 5770000;
                double va = VecAdd(size);
                unit += va / (vaCost * size);
                //Console.WriteLine(unit);
                size = 100;
                double mm = MatMul(size);
                unit += mm / (mmCost * size * size * size);
                //Console.WriteLine(unit);
                size = 1000;
                double mv = MatxVec(1000);
                unit += mv / (mvCost * size * size);
                //Console.WriteLine(unit);
                //Console.WriteLine("VecAdd: {0}", va);
                //Console.WriteLine("MatMul: {0}", mm);
                //Console.WriteLine("MatxVec: {0}", mv);
                UnitInMs = unit / 3.0; 
            }
            
            private double VecAdd(int size)
            {
                int[] input1 = new int[size];
                int[] input2 = new int[size];
                for (int i = 0; i < size; i++)
                {
                    input1[i] = 5;
                    input2[i] = 5;
                }
                int[] output = new int[size];

                QPerfCounter q = new QPerfCounter(MagTime.msec);
                q.Start();
                for (int i = 0; i < size; i++)
                    output[i] = input1[i] + input2[i];
                q.Stop();
                return q.Duration(1);
            }

            private double MatMul(int size)
            {
                int[,] matrix1 = new int[size, size];
                int[,] matrix2 = new int[size, size];
                for (int i = 0; i < size; i++)
                    for (int j = 0; j < size; j++)
                    {
                        matrix1[i, j] = 5;
                        matrix2[i, j] = 5;
                    }

                int[,] output = new int[size, size];

                QPerfCounter q = new QPerfCounter(MagTime.msec);
                q.Start();
                for (int i = 0; i < size; i++)
                    for (int j = 0; j < size; j++)
                        for (int h = 0; h < size; h++)
                            output[i, j] += matrix1[i, h] * matrix2[h, j];
                q.Stop();
                return q.Duration(1);
            }

            private double MatxVec(int size)
            {
                int[] input1 = new int[size];
                for (int i = 0; i < size; i++)
                {
                    input1[i] = 5;
                }
                int[,] matrix1 = new int[size, size];
                for (int i = 0; i < size; i++)
                    for (int j = 0; j < size; j++)
                    {
                        matrix1[i, j] = 5;
                    }

                int[] output = new int[size];

                QPerfCounter q = new QPerfCounter(MagTime.msec);
                q.Start();
                for (int i = 0; i < size; i++)
                    for (int j = 0; j < size; j++)
                        output[i] += input1[j] * matrix1[i, j];
                q.Stop();
                return q.Duration(1);
            }
        }
    }
}
